              package com.java.diagnostics.visualizer.displayer.html;
              
              import com.java.diagnostics.visualizer.data.AggregateData;
              import com.java.diagnostics.visualizer.data.DataSet;
              import com.java.diagnostics.visualizer.data.StructuredData;
              import com.java.diagnostics.visualizer.data.Variant;
              import com.java.diagnostics.visualizer.data.ids.ID;
              import com.java.diagnostics.visualizer.impl.axes.NumberFormatter;
              import java.io.BufferedWriter;
              import java.io.IOException;
              import java.util.Iterator;
              import java.util.Map;
              import java.util.Map.Entry;
              import java.util.Set;
              import java.util.TreeSet;
              
              public class StructuredDataWriter extends DataWriter
              {
                public StructuredDataWriter(DataSet dataSet, AggregateData representativeData, BufferedWriter buffer, ID id)
                {
                  super(dataSet, id, representativeData, buffer);
                }
              
                public void writeData() throws IOException
                {
                  this.usingTable = true;
                  addDataHeading();
              
                  openTable();
              
                  Iterator labels = new TreeSet(
                    this.representativeData.getStructuredData(this.id)
                    .getColumnNames()).iterator();
              
                  writeColumnHeadings(this.representativeData.getLabel());
              
                  while (labels.hasNext()) {
                    String key = (String)labels.next();
                    openRow();
                    this.oddRow = (!this.oddRow);
                    this.buffer.write(NEWLINE);
                    addTableCell(key);
              
                    Variant[] variants = this.dataSet.getVariants();
                    for (int i = 0; i < variants.length; i++) {
                      StructuredData data = variants[i].getStructuredData(this.id);
                      addRow(key, data);
                    }
                    closeRow();
                  }
                  closeTable();
                }
              
                protected void writeColumnHeadings(String label) throws IOException
                {
                  this.buffer.write(NEWLINE);
                  if (this.dataSet.getVariants().length > 1) {
                    openRow();
                    addTableHeader(VARIANT, 1);
                    Variant[] variants = this.dataSet.getVariants();
                    for (int i = 0; i < variants.length; i++) {
                      Variant variant = variants[i];
                      addHorizontalTableHeader(variant.getLabel(), 1);
                    }
                    closeRow();
                  }
                }
              
                protected void addRow(String key, StructuredData data) throws IOException {
                  if (data != null)
                  {
                    Object value = data.getContents().get(key);
              
                    if (value != null) {
                      if ((value instanceof Double))
                        addTableCell(NumberFormatter.prettyString(
                          ((Double)value).doubleValue()));
                      else if ((value instanceof Map)) {
                        writeNestedMaps((Map)value);
                      }
                      else
                        addTableCell(value.toString());
                    }
                    else
                      addTableCell("N/A");
                  }
                  else
                  {
                    addTableCell("N/A");
                  }
                }
              
                private void writeNestedMaps(Map map) throws IOException {
                  openTable();
                  Iterator entries = map.entrySet().iterator();
                  while (entries.hasNext()) {
                    Map.Entry entry = (Map.Entry)entries.next();
                    Object name = entry.getKey();
                    Object thing = entry.getValue();
                    if (!(thing instanceof Map))
                    {
                      addTableHeader(name.toString(), 1);
                      addTableCell(thing.toString());
                    }
                  }
                  closeTable();
                  openTable();
                  entries = map.entrySet().iterator();
                  while (entries.hasNext()) {
                    Map.Entry entry = (Map.Entry)entries.next();
                    Object name = entry.getKey();
                    Object thing = entry.getValue();
                    if ((thing instanceof Map))
                    {
                      addHorizontalTableHeader(name.toString(), ((Map)thing).size() * 2);
                      writeNestedMaps((Map)thing);
                    }
                  }
                  closeTable();
                }
              }

